package com.pearl.generator.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.builder.ConfigBuilder;
import com.baomidou.mybatisplus.generator.config.po.LikeTable;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.pearl.generator.common.enums.DateTypeEnum;
import com.pearl.generator.common.enums.YesOrNo;
import com.pearl.generator.mybatisplus.EnhanceFreemarkerTemplateEngine;
import com.pearl.generator.pojo.entity.*;
import com.pearl.generator.pojo.request.GeneratorCodeRequest;
import com.pearl.generator.pojo.request.PreviewCodeRequest;
import com.pearl.generator.service.*;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;

/**
 * @author TangDan
 * @version 1.0
 * @since 2022/8/8
 */
@Service
public class GeneratorCodeServiceImpl implements GeneratorCodeService {

    @Resource
    IDataSourceService dataSourceService;
    @Resource
    GlobalConfigService globalConfigService;
    @Resource
    PackageConfigService packageConfigService;
    @Resource
    StrategyConfigService strategyConfigService;
    @Resource
    EntityConfigService entityConfigService;
    @Resource
    ControllerConfigService controllerConfigService;
    @Resource
    ServiceConfigService serviceConfigService;
    @Resource
    MapperConfigService mapperConfigService;
    @Resource
    PojoConfigService pojoConfigService;
    @Resource
    IGeneratorPlanConfigService planConfigService;

    @Override
    public void generator(GeneratorCodeRequest request) {
        // 指定引擎 生成代码
        AutoGenerator autoGenerator = getGeneratorConfig(request);
        autoGenerator.execute(new EnhanceFreemarkerTemplateEngine());
    }


    @Override
    public Map<String, String> preview(PreviewCodeRequest request) {
        GeneratorCodeRequest generatorCodeRequest = new GeneratorCodeRequest();
        generatorCodeRequest.setDatasourceId(request.getDatasourceId());
        generatorCodeRequest.setPlanId(request.getPlanId());
        generatorCodeRequest.setTableNameList(CollUtil.toList(request.getTableName()));
        AutoGenerator autoGenerator = getGeneratorConfig(generatorCodeRequest);
        ConfigBuilder configBuilder = new ConfigBuilder(
                autoGenerator.getPackageInfo(),
                autoGenerator.getDataSource(),
                autoGenerator.getStrategy(),
                autoGenerator.getTemplate(),
                autoGenerator.getGlobalConfig(),
                autoGenerator.getInjectionConfig());
        EnhanceFreemarkerTemplateEngine templateEngine = new EnhanceFreemarkerTemplateEngine();
        templateEngine.init(configBuilder);
        templateEngine.setConfigBuilder(configBuilder);
        return templateEngine.preview();
    }

    /**
     * 转化配置
     *
     * @param request 数据源、方案、表
     * @return AutoGenerator
     */
    private AutoGenerator getGeneratorConfig(GeneratorCodeRequest request) {
        // 1. 查询方案
        GeneratorPlanConfigEntity plan = planConfigService.getById(request.getPlanId());
        Assert.notNull(plan, "未查询到当前方案");
        // 2. 数据源配置
        DataSourceEntity dataSource = dataSourceService.getById(request.getDatasourceId());
        Assert.notNull(dataSource, "未查询到当前数据源！");
        DataSourceConfig dataSourceConfig = new DataSourceConfig.Builder(dataSource.getUrl(), dataSource.getUsername(), dataSource.getPassword()).build();
        // 3. 策略配置
        StrategyConfig.Builder strategyConfigBuilder = getStrategyConfigBuilder(plan.getStrategyId(), request.getTableNameList());
        setControllerConfig(plan.getControllerId(), strategyConfigBuilder);// Controller 配置
        setServiceConfig(plan.getServiceId(), strategyConfigBuilder);// Service 配置
        setEntityConfig(plan.getEntityId(), strategyConfigBuilder);// 实体类配置
        setMapperConfig(plan.getMapperId(), strategyConfigBuilder);// Mapper 配置
        // 4. 生成配置
        AutoGenerator autoGenerator = new AutoGenerator(dataSourceConfig); // 数据源配置
        GeneratorGlobalConfig generatorGlobalConfig = globalConfigService.getById(plan.getGlobalId());
        Assert.notNull(generatorGlobalConfig, "未查询到全局配置！");
        autoGenerator.global(getGlobalConfig(generatorGlobalConfig)); // 全局配置
        autoGenerator.packageInfo(getPackageConfig(plan.getPackageId())); // 包配置
        autoGenerator.strategy(strategyConfigBuilder.build()); // 策略配置
        autoGenerator.template(getTemplateConfig()); // 配置模板
        autoGenerator.injection(getPojoInjectionConfig(plan.getPojoId(), generatorGlobalConfig.getLombok(), plan.getControllerId()));    // 自定义POJO 配置
        return autoGenerator;
    }

    /**
     * 获取全局配置
     *
     * @param generatorGlobalConfig 全局配置
     * @return GlobalConfig
     */
    private GlobalConfig getGlobalConfig(GeneratorGlobalConfig generatorGlobalConfig) {
        GlobalConfig.Builder globalConfigBuilder = new GlobalConfig.Builder();
        if (YesOrNo.isTrue(generatorGlobalConfig.getFileOverride())) {
            globalConfigBuilder.fileOverride();// 文件覆盖
        }
        if (YesOrNo.isTrue(generatorGlobalConfig.getDisableOpenDir())) {
            globalConfigBuilder.disableOpenDir();// 禁止打开输出目录
        }
        if (YesOrNo.isTrue(generatorGlobalConfig.getKotlin())) {
            globalConfigBuilder.enableKotlin();// 开启 kotlin 模式
        }
        if (YesOrNo.isTrue(generatorGlobalConfig.getSwagger())) {
            globalConfigBuilder.enableSwagger();// 开启 swagger 模式
        }
        // 日期类型处理
        DateType dateType;
        String requestDateType = generatorGlobalConfig.getDateType();
        if (StrUtil.isNotBlank(requestDateType)) {
            if (DateTypeEnum.ONLY_DATE.getValue().equals(requestDateType)) {
                dateType = DateType.ONLY_DATE;
            } else if (DateTypeEnum.SQL_PACK.getValue().equals(requestDateType)) {
                dateType = DateType.SQL_PACK;
            } else {
                dateType = DateType.TIME_PACK;
            }
        } else {
            dateType = DateType.ONLY_DATE;
        }
        generatorGlobalConfig.setDateType("");
        return globalConfigBuilder
                .outputDir(generatorGlobalConfig.getOutputDir()) // 指定输出目录
                .author(generatorGlobalConfig.getAuthor()) // 作者名
                .commentDate(generatorGlobalConfig.getCommentDate()) // 注释日期
                .dateType(dateType)   // 时间策略
                .build();
    }

    /**
     * 获取包名配置
     *
     * @param id 包名配置主键ID
     * @return PackageConfig
     */
    private PackageConfig getPackageConfig(Long id) {
        // 3. 包配置
        GeneratorPackageConfig generatorPackageConfig = packageConfigService.getById(id);
        Assert.notNull(generatorPackageConfig, "未查询到包名配置！");
        return new PackageConfig.Builder()
                .parent(generatorPackageConfig.getParent()) // 父包名
                .moduleName(generatorPackageConfig.getModuleName()) // 父包模块名
                .entity(generatorPackageConfig.getEntity()) // Entity 包名
                .service(generatorPackageConfig.getService()) // Service 包名
                .serviceImpl(generatorPackageConfig.getServiceImpl()) // Service Impl 包名
                .controller(generatorPackageConfig.getController())  // Controller 包名
                .mapper(generatorPackageConfig.getMapper()) // Mapper 包名
                .xml(generatorPackageConfig.getXml()) // Mapper XML 包名
                .other(generatorPackageConfig.getOther()) // 自定义文件包名	输出自定义文件时所用到的包名
                //.pathInfo() // 路径配置信息
                .build();
    }

    /**
     * 获取策略配置构建者
     *
     * @param id            主键ID
     * @param tableNameList 表名集合
     * @return 构建者
     */
    private StrategyConfig.Builder getStrategyConfigBuilder(Long id, List<String> tableNameList) {
        GeneratorStrategyConfig generatorStrategyConfig = strategyConfigService.getById(id);
        Assert.notNull(generatorStrategyConfig, "未查询到策略配置！");
        StrategyConfig.Builder strategyConfigBuilder = new StrategyConfig.Builder();
        if (YesOrNo.isTrue(generatorStrategyConfig.getEnableCapitalMode())) {
            strategyConfigBuilder.enableCapitalMode();// 开启大写命名
        }
        if (YesOrNo.isTrue(generatorStrategyConfig.getEnableSkipView())) {
            strategyConfigBuilder.enableSkipView();// 开启跳过视图
        }
        if (YesOrNo.isTrue(generatorStrategyConfig.getDisableSqlFilter())) {
            strategyConfigBuilder.disableSqlFilter();// 禁用 sql 过滤，语法不能支持使用 sql 过滤表的话，可以考虑关闭此开关
        }
        if (YesOrNo.isTrue(generatorStrategyConfig.getEnableSchema())) {
            strategyConfigBuilder.enableSchema();// 启用 schema
        }
        if (StrUtil.isNotBlank(generatorStrategyConfig.getLikeTable())) {
            strategyConfigBuilder.likeTable(new LikeTable(generatorStrategyConfig.getLikeTable())); // 模糊表匹配(sql 过滤)
        }
        //strategyConfigBuilder.notLikeTable(new LikeTable(generatorStrategyConfig.getNotLikeTable())); // 模糊表排除(sql 过滤)
        // 添加选择的表
        if (CollUtil.isEmpty(tableNameList)) {
            throw new IllegalArgumentException("表名不能为空");
        }
        strategyConfigBuilder.addInclude(tableNameList);
        //strategyConfigBuilder.addExclude(StrUtil.split(generatorStrategyConfig.getAddExclude(), StrUtil.COMMA));// 增加表排除匹配(内存过滤)
        if (StrUtil.isNotBlank(generatorStrategyConfig.getAddTablePrefix())) {
            strategyConfigBuilder.addTablePrefix(generatorStrategyConfig.getAddTablePrefix()); // 增加过滤表前缀
        }
        if (StrUtil.isNotBlank(generatorStrategyConfig.getAddTableSuffix())) {
            strategyConfigBuilder.addTableSuffix(generatorStrategyConfig.getAddTableSuffix()); // 增加过滤表后缀
        }
        if (StrUtil.isNotBlank(generatorStrategyConfig.getAddFieldPrefix())) {
            strategyConfigBuilder.addFieldPrefix(); // 增加过滤字段前缀
        }
        if (StrUtil.isNotBlank(generatorStrategyConfig.getAddFieldSuffix())) {
            strategyConfigBuilder.addFieldSuffix(generatorStrategyConfig.getAddFieldSuffix()); // 增加过滤字段后缀
        }
        return strategyConfigBuilder;
    }

    /**
     * 设置实体类配置
     *
     * @param id                    主键ID
     * @param strategyConfigBuilder 策略配置构建者
     */
    private void setEntityConfig(Long id, StrategyConfig.Builder strategyConfigBuilder) {
        // 5. 实体类Entity配置
        GeneratorEntityConfig entityConfig = entityConfigService.getById(id);
        Assert.notNull(entityConfig, "未查询到实体类配置！");
        if (YesOrNo.isTrue(entityConfig.getDisableSerialVersionUid())) {
            strategyConfigBuilder.entityBuilder().disableSerialVersionUID();// 禁用生成 serialVersionUID
        }
        if (YesOrNo.isTrue(entityConfig.getEnableColumnConstant())) {
            strategyConfigBuilder.entityBuilder().enableColumnConstant();// 开启生成字段常量
        }
/*        if (YesOrNo.isTrue(entityConfig.getEnableChainModel())) {
            strategyConfigBuilder.entityBuilder().enableChainModel();// 开启链式模型
        }
        if (YesOrNo.isTrue(entityConfig.getEnableLombok())) {
            strategyConfigBuilder.entityBuilder().enableLombok();// 开启 lombok 模型
        }*/
        if (YesOrNo.isTrue(entityConfig.getEnableRemoveIsPrefix())) {
            strategyConfigBuilder.entityBuilder().enableRemoveIsPrefix();// 开启 Boolean 类型字段移除 is 前缀
        }
        if (YesOrNo.isTrue(entityConfig.getEnableTableFieldAnnotation())) {
            strategyConfigBuilder.entityBuilder().enableTableFieldAnnotation();// 开启生成实体时生成字段注解@TableField
        }
        if (YesOrNo.isTrue(entityConfig.getEnableActiveRecord())) {
            strategyConfigBuilder.entityBuilder().enableActiveRecord();// 开启 ActiveRecord 模型
        }
        // 设置主键类型
        IdType idTypeEnum;
        Integer idType = entityConfig.getIdType();
        switch (idType) {
            case 1:
                idTypeEnum = IdType.NONE;
                break;
            case 2:
                idTypeEnum = IdType.INPUT;
                break;
            case 3:
                idTypeEnum = IdType.ASSIGN_ID;
                break;
            case 4:
                idTypeEnum = IdType.ASSIGN_UUID;
                break;
            default:
                idTypeEnum = IdType.AUTO;
        }
        strategyConfigBuilder.entityBuilder()
                .superClass(entityConfig.getSuperClass()) // 设置父类
                .versionColumnName(entityConfig.getVersionColumnName())// 乐观锁字段名(数据库)
                .versionPropertyName(entityConfig.getVersionPropertyName())// 乐观锁属性名(实体)
                .logicDeleteColumnName(entityConfig.getLogicDeleteColumnName())// 逻辑删除字段名(数据库)
                .logicDeletePropertyName(entityConfig.getLogicDeletePropertyName())// 逻辑删除属性名(实体)
                //.naming(NamingStrategy.underline_to_camel)// 数据库表映射到实体的命名策略 统一设置为下划线转驼峰即可
                //.columnNaming(null)// 数据库表字段映射到实体的命名策略,默认为 null，未指定按照 naming 执行
                .addSuperEntityColumns(StrUtil.split(entityConfig.getAddSuperEntityColumns(), StrUtil.COMMA))// 添加父类公共字段
                .addIgnoreColumns(StrUtil.split(entityConfig.getAddIgnoreColumns(), StrUtil.COMMA))// 添加忽略字段
                //.addTableFills()// 添加表字段填充
                .idType(idTypeEnum)// 全局主键类型
                //.convertFileName()// 转换文件名称
                .formatFileName(entityConfig.getFormatFileName());//格式化文件名称
    }

    /**
     * 设置Controller 策略配置
     *
     * @param id                    主键ID
     * @param strategyConfigBuilder 策略配置构建者
     */
    private void setControllerConfig(Long id, StrategyConfig.Builder strategyConfigBuilder) {
        GeneratorControllerConfig controllerConfig = controllerConfigService.getById(id);
        Assert.notNull(controllerConfig, "未查询到Controller配置！");
        if (YesOrNo.isTrue(controllerConfig.getEnableRestStyle())) {
            strategyConfigBuilder.controllerBuilder().enableRestStyle();// 开启生成@RestController 控制器
        }
        if (YesOrNo.isTrue(controllerConfig.getEnableHyphenStyle())) {
            strategyConfigBuilder.controllerBuilder().enableHyphenStyle();// 开启驼峰转连字符
        }
        strategyConfigBuilder.controllerBuilder()
                .superClass(controllerConfig.getSuperClass())
                .formatFileName(controllerConfig.getFormatFileName());// 格式化文件名称
    }

    /**
     * 设置Service 策略配置
     *
     * @param id                    主键ID
     * @param strategyConfigBuilder 策略配置构建者
     */
    private void setServiceConfig(Long id, StrategyConfig.Builder strategyConfigBuilder) {
        GeneratorServiceConfig serviceConfig = serviceConfigService.getById(id);
        Assert.notNull(serviceConfig, "未查询到Service配置！");
        if (StrUtil.isNotEmpty(serviceConfig.getSuperServiceClass())) {
            strategyConfigBuilder.serviceBuilder()
                    .superServiceClass(serviceConfig.getSuperServiceClass());
        }
        if (StrUtil.isNotEmpty(serviceConfig.getSuperServiceImplClass())) {
            strategyConfigBuilder.serviceBuilder()
                    .superServiceClass(serviceConfig.getSuperServiceImplClass());
        }
        strategyConfigBuilder.serviceBuilder()
                //.convertServiceFileName() //
                // .convertServiceImplFileName()//
                .formatServiceFileName(serviceConfig.getFormatServiceFileName())//格式化 service 接口文件名称
                .formatServiceImplFileName(serviceConfig.getFormatServiceImplFileName());// 格式化 service 实现类文件名称
    }

    /**
     * 设置Mapper 策略配置
     *
     * @param id                    主键ID
     * @param strategyConfigBuilder 策略配置构建者
     */
    private void setMapperConfig(Long id, StrategyConfig.Builder strategyConfigBuilder) {
        GeneratorMapperConfig mapperConfig = mapperConfigService.getById(id);
        Assert.notNull(mapperConfig, "未查询到 Mapper配置！");
        if (YesOrNo.isTrue(mapperConfig.getEnableMapperAnnotation())) {
            strategyConfigBuilder.mapperBuilder().enableMapperAnnotation();// 开启 @Mapper 注解
        }
        if (YesOrNo.isTrue(mapperConfig.getEnableBaseColumnList())) {
            strategyConfigBuilder.mapperBuilder().enableBaseColumnList();// 启用 BaseResultMap 生成
        }
        if (YesOrNo.isTrue(mapperConfig.getEnableBaseResultMap())) {
            strategyConfigBuilder.mapperBuilder().enableBaseResultMap();// 启用 BaseColumnList
        }
        if (StrUtil.isNotEmpty(mapperConfig.getSuperClass())) {
            strategyConfigBuilder.mapperBuilder()
                    .superClass(mapperConfig.getSuperClass());
        }
        strategyConfigBuilder.mapperBuilder()
                //.superClass(mapperConfig.getSuperClass())//
                //.cache("")// 设置缓存实现类
                //.convertMapperFileName//转换 mapper 类文件名称
                //.convertXmlFileName// 转换 xml 文件名称
                .formatMapperFileName(mapperConfig.getFormatMapperFileName())// 格式化 mapper 文件名称
                .formatXmlFileName(mapperConfig.getFormatXmlFileName()).build();//格式化 xml 实现类文件名称
    }

    /**
     * 获取模板
     *
     * @return 配置模板 TemplateConfig
     */
    private TemplateConfig getTemplateConfig() {
        return new TemplateConfig.Builder()
                .entity("/templates/generator/entity.java")
                .service("/templates/generator/service.java")
                .serviceImpl("/templates/generator/serviceImpl.java")
                .mapper("/templates/generator/mapper.java")
                .xml("/templates/generator/mapper.xml")
                .controller("/templates/generator/controller.java")
                .build();
    }

    /**
     * 获取自定义注入配置
     *
     * @param customMap     自定义变量
     * @param customFileMap 自定义模板文件
     * @return InjectionConfig
     */
    private InjectionConfig getInjectionConfig(Map<String, Object> customMap, Map<String, String> customFileMap) {
        return new InjectionConfig.Builder()
                .beforeOutputFile((tableInfo, objectMap) -> {
                    System.out.println("tableInfo: " + tableInfo.getEntityName() + " objectMap: " + objectMap.size());
                })
                .customMap(customMap) // 添加到模板中的属性
                .customFile(customFileMap)
                .build();
    }

    /**
     * 获取POJO 自定义注入配置
     *
     * @param id           POJO配置类ID
     * @param enableLombok 全局开启Lombok
     * @return POJO 自定义注入配置
     */
    private InjectionConfig getPojoInjectionConfig(Long id, Integer enableLombok, Long controllerId) {
        GeneratorControllerConfig controllerConfig = controllerConfigService.getById(controllerId);
        Assert.notNull(controllerConfig, "未查询到Controller配置！");
        String resultClass = controllerConfig.getResultClass();
        GeneratorPojoConfig pojoConfig = pojoConfigService.getById(id);
        Assert.notNull(pojoConfig, "未查询到POJO生成配置！");
        Map<String, String> customFileMap = new HashMap<>();  // 文件后缀：模板位置
        if (YesOrNo.isTrue(pojoConfig.getEnableBo())) { // 开启DTO生成
            customFileMap.put("BO.java", "/templates/generator/pojo.java.ftl");
        }
        Map<String, Object> customMap = new HashMap<>();  // objectMap添加数据，可在模板中获取
        customMap.put("resultClass", resultClass);
        customMap.put("resultName", resultClass.substring(resultClass.lastIndexOf(".") + 1));
        if (YesOrNo.isTrue(pojoConfig.getEnableMapStruct())) { // 开启对象映射MapStruct
            customMap.put("enableMapStruct", true);
        } else {
            customMap.put("enableMapStruct", false);
        }
        if (YesOrNo.isTrue(pojoConfig.getEnableQuery())) { // 开启Query
            customFileMap.put("Query.java", "/templates/generator/pojo.java.ftl");
            customMap.put("enableQuery", true);
            if (StrUtil.isNotBlank(pojoConfig.getQuerySuperClass())) {
                customMap.put("enablePojoQuerySuperClass", true); // 查询参数父类
                customMap.put("pojoQuerySuperClass", pojoConfig.getQuerySuperClass()); // 查询参数父类
            }
        } else {
            customMap.put("enableQuery", false);
        }
        if (YesOrNo.isTrue(pojoConfig.getEnableVo())) { // 开启VO
            customFileMap.put("VO.java", "/templates/generator/pojo.java.ftl");
            customMap.put("enableVo", true);
        } else {
            customMap.put("enableVo", false);
        }
        if (YesOrNo.isTrue(pojoConfig.getEnableDto())) { // 开启Dto
            customFileMap.put("DTO.java", "/templates/generator/pojo.java.ftl");
            customMap.put("enableDto", true);
        } else {
            customMap.put("enableDto", false);
        }
        if (YesOrNo.isTrue(pojoConfig.getEnableRequest())) { // 开启Request
            customFileMap.put("Request.java", "/templates/generator/pojo.java.ftl");
            customMap.put("enableRequest", true);
        } else {
            customMap.put("enableRequest", false);
        }
        if (YesOrNo.isTrue(pojoConfig.getEnableForm())) { // 开启Form
            customMap.put("enableForm", true);
            customFileMap.put("SaveForm.java", "/templates/generator/pojo.java.ftl"); // 新增表单
            customFileMap.put("UpdateForm.java", "/templates/generator/pojo.java.ftl");// 修改表单
        } else {
            customMap.put("enableForm", false);
        }
        if (YesOrNo.isTrue(pojoConfig.getEnableBo())) { // 开启Bo
            customFileMap.put("BO.java", "/templates/generator/pojo.java.ftl");
            customMap.put("enableBo", true);
        } else {
            customMap.put("enableBo", false);
        }
        if (YesOrNo.isTrue(enableLombok)) { // 全局开启Lombok
            customMap.put("lombok", true);
        } else {
            customMap.put("lombok", false);
        }
        if (YesOrNo.isTrue(pojoConfig.getDisableSerialVersionUid())) { // 禁用生成 serialVersionUID
            customMap.put("pojoSerialVersionUID", false);
        } else {
            customMap.put("pojoSerialVersionUID", true);
        }
        return getInjectionConfig(customMap, customFileMap);
    }

}
